/*  This file is part of Phantom Power.
 *  Copyright (C) 2010 Jacob Dawid
 *
 *  Phantom Power is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Phantom Power is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Phantom Power. If not, see <http://www.gnu.org/licenses/>.
 */

#include "device.h"
#include <iostream>

device::device()
{
	settings=new QSettings("phantom-power.configuration", QSettings::IniFormat);
	#ifdef __unix__
		  linuxDeviceInitialize();
	#endif
	#ifdef __windows__
		  windowsDeviceInitialize();
	#endif
}

device::~device()
{
	#ifdef __unix__
		  linuxDeviceDestroy();
	#endif
	#ifdef __windows__
		  windowsDeviceInitialize();
	#endif

	delete settings;
}

#ifdef __unix__
	void device::linuxDeviceInitialize()
	{
		system("modprobe uinput");
		deviceHandle=open("/dev/uinput", O_WRONLY|O_NONBLOCK);
		if(deviceHandle<0)
		{
			std::cout<<"Error opening device handle. Are you root?"<<std::endl;
			return;	
		}
	
		struct uinput_user_dev uidev;		
		memset(&uidev, 0, sizeof(uidev));
		snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "droidpad_client");
		uidev.id.bustype = BUS_USB;
		uidev.id.vendor  = 0x1234;
		uidev.id.product = 0x1;
		uidev.id.version = 1;	
		write(deviceHandle, &uidev, sizeof(uidev));
	}
	
	void device::linuxDeviceDestroy()
	{		
		linuxIO(UI_DEV_DESTROY);
		close(deviceHandle);
		system("rmmod uinput");
	}
	
	void device::linuxDeviceReset()
	{
		linuxDeviceDestroy();
		linuxDeviceInitialize();
	}

	void device::linuxCreateMouseDevice()
	{
		linuxDeviceReset();
		
		linuxIO(UI_SET_EVBIT, EV_SYN);

		linuxIO(UI_SET_EVBIT, EV_KEY);
		linuxIO(UI_SET_KEYBIT, BTN_LEFT);
		linuxIO(UI_SET_KEYBIT, BTN_MIDDLE);
		linuxIO(UI_SET_KEYBIT, BTN_RIGHT);

		linuxIO(UI_SET_EVBIT, EV_REL);
		linuxIO(UI_SET_RELBIT, REL_X);
		linuxIO(UI_SET_RELBIT, REL_Y);

		linuxIO(UI_DEV_CREATE);
	}

	void device::linuxCreateJoystickDevice()
	{
		linuxDeviceReset();

		linuxIO(UI_SET_EVBIT, EV_SYN);

		linuxIO(UI_SET_EVBIT, EV_KEY);
		linuxIO(UI_SET_KEYBIT, BTN_A);
		linuxIO(UI_SET_KEYBIT, BTN_B);
		linuxIO(UI_SET_KEYBIT, BTN_C);
		linuxIO(UI_SET_KEYBIT, BTN_X);
		linuxIO(UI_SET_KEYBIT, BTN_Y);
		linuxIO(UI_SET_KEYBIT, BTN_Z);
		linuxIO(UI_SET_KEYBIT, BTN_TL);
		linuxIO(UI_SET_KEYBIT, BTN_TR);
		linuxIO(UI_SET_KEYBIT, BTN_TL2);
		linuxIO(UI_SET_KEYBIT, BTN_TR2);
		linuxIO(UI_SET_KEYBIT, BTN_SELECT);
		linuxIO(UI_SET_KEYBIT, BTN_START);
		linuxIO(UI_SET_KEYBIT, BTN_MODE);
		linuxIO(UI_SET_KEYBIT, BTN_THUMBL);
		linuxIO(UI_SET_KEYBIT, BTN_THUMBR);

		linuxIO(UI_SET_KEYBIT, BTN_TRIGGER);
		linuxIO(UI_SET_KEYBIT, BTN_THUMB);
		linuxIO(UI_SET_KEYBIT, BTN_THUMB2);
		linuxIO(UI_SET_KEYBIT, BTN_TOP);
		linuxIO(UI_SET_KEYBIT, BTN_TOP2);
		linuxIO(UI_SET_KEYBIT, BTN_PINKIE);
		linuxIO(UI_SET_KEYBIT, BTN_BASE);
		linuxIO(UI_SET_KEYBIT, BTN_BASE2);
		linuxIO(UI_SET_KEYBIT, BTN_BASE3);
		linuxIO(UI_SET_KEYBIT, BTN_BASE4);
		linuxIO(UI_SET_KEYBIT, BTN_BASE5);
		linuxIO(UI_SET_KEYBIT, BTN_BASE6);
		linuxIO(UI_SET_KEYBIT, BTN_DEAD);
	
		linuxIO(UI_SET_EVBIT, EV_ABS);
		linuxIO(UI_SET_ABSBIT,ABS_X);
		linuxIO(UI_SET_ABSBIT,ABS_Y);
		linuxIO(UI_SET_ABSBIT,ABS_Z);
		linuxIO(UI_SET_ABSBIT,ABS_RX);
		linuxIO(UI_SET_ABSBIT,ABS_RY);
		linuxIO(UI_SET_ABSBIT,ABS_RZ);
		linuxIO(UI_SET_ABSBIT,ABS_THROTTLE);
		linuxIO(UI_SET_ABSBIT,ABS_RUDDER);
		linuxIO(UI_SET_ABSBIT,ABS_WHEEL);
		linuxIO(UI_SET_ABSBIT,ABS_GAS);
		linuxIO(UI_SET_ABSBIT,ABS_BRAKE);
		linuxIO(UI_SET_ABSBIT,ABS_HAT0X);
		linuxIO(UI_SET_ABSBIT,ABS_HAT0Y);
		linuxIO(UI_SET_ABSBIT,ABS_HAT1X);
		linuxIO(UI_SET_ABSBIT,ABS_HAT1Y);
		linuxIO(UI_SET_ABSBIT,ABS_HAT2X);
		linuxIO(UI_SET_ABSBIT,ABS_HAT2Y);
		linuxIO(UI_SET_ABSBIT,ABS_HAT3X);
		linuxIO(UI_SET_ABSBIT,ABS_HAT3Y);
		linuxIO(UI_SET_ABSBIT,ABS_PRESSURE);
		linuxIO(UI_SET_ABSBIT,ABS_DISTANCE);
		linuxIO(UI_SET_ABSBIT,ABS_TILT_X);
		linuxIO(UI_SET_ABSBIT,ABS_TILT_Y);
		linuxIO(UI_SET_ABSBIT,ABS_TOOL_WIDTH);
		linuxIO(UI_SET_ABSBIT,ABS_VOLUME);
		linuxIO(UI_SET_ABSBIT,ABS_MISC);

		linuxIO(UI_DEV_CREATE);
	}

	void device::linuxCreateKeyboardDevice()
	{
		linuxDeviceReset();

		linuxIO(UI_SET_EVBIT, EV_SYN);

		linuxIO(UI_SET_EVBIT, EV_KEY);
		for(int i=0;i<128;i++)
			linuxIO(UI_SET_KEYBIT, i);

		linuxIO(UI_DEV_CREATE);
	}

	void device::linuxHandleMouseIssue(QList<double> issue)
	{
		linuxSendEvent(EV_REL, REL_X, issue[0]);
		linuxSendEvent(EV_REL, REL_Y, issue[1]);
		linuxSendEvent(EV_KEY, BTN_LEFT, issue[3]);
		linuxSendEvent(EV_KEY, BTN_MIDDLE, issue[4]);
		linuxSendEvent(EV_KEY, BTN_RIGHT, issue[5]);
		linuxSynchronize();
	}

	void device::linuxHandleSliderIssue(QList<double> issue)
	{
	}

	void device::linuxHandleJoystick1Issue(QList<double> issue)
	{

	}

	void device::linuxHandleJoystick2Issue(QList<double> issue)
	{
	}

	void device::linuxHandleJoystick2bIssue(QList<double> issue)
	{
	}

	void device::linuxIO(int request, int argument)
	{
		if(deviceHandle)
			ioctl(deviceHandle, request, argument);
	}

	void device::linuxIO(int request)
	{
		if(deviceHandle)
			ioctl(deviceHandle, request);
	}

	void device::linuxSendEvent(int type, int code, int value)
	{
		struct input_event inputEvent;
		memset(&inputEvent, 0, sizeof(inputEvent));
		inputEvent.type=type;
		inputEvent.code=code;
		inputEvent.value=value;
		write(deviceHandle, &inputEvent, sizeof(inputEvent));
	}

	void device::linuxSynchronize()
	{
		linuxSendEvent(EV_SYN, 0, 0);
	}
#endif

#ifdef __windows__
	void device::windowsDeviceInitialize()
	{
	}

	void device::windowsDeviceDestroy()
	{
		// - empty
	}

	void device::windowsDeviceReset()
	{
		windowsDeviceDestroy();
		windowsDeviceInitialize();
	}

	void device::windowsHandleMouseIssue(QList<double> issue)
	{
		static bool lastLeftButton=0;
		static bool lastMiddleButton=0;
		static bool lastRightButton=0;

		INPUT input;
		memset(&input, 0, sizeof(input));
		input.type=INPUT_MOUSE;
		input.mi.dx=(int)issue[0];
		input.mi.dy=(int)issue[1];
		input.mi.dwFlags=MOUSEEVENTF_MOVE;

		if(issue[3]&&!lastLeftButton)
			input.mi.dwFlags|=MOUSEEVENTF_LEFTDOWN;
		if(!issue[3]&&lastLeftButton)
			input.mi.dwFlags|=MOUSEEVENTF_LEFTUP;

		if(issue[4]&&!lastMiddleButton)
			input.mi.dwFlags|=MOUSEEVENTF_MIDDLEDOWN;
		if(!issue[4]&&lastMiddleButton)
			input.mi.dwFlags|=MOUSEEVENTF_MIDDLEUP;

		if(issue[5]&&!lastRightButton)
			input.mi.dwFlags|=MOUSEEVENTF_RIGHTDOWN;
		if(!issue[5]&&lastRightButton)
			input.mi.dwFlags|=MOUSEEVENTF_RIGHTUP;

		SendInput(1, &input, sizeof(input));

		lastLeftButton=issue[3];
		lastMiddleButton=issue[4];
		lastRightButton=issue[5];
	}

	void device::windowsHandleSliderIssue(QList<double> issue)
	{
	}
	
	void device::windowsHandleJoystick1Issue(QList<double> issue)
	{
	}

	void device::windowsHandleJoystick2Issue(QList<double> issue)
	{
	}

	void device::windowsHandleJoystick2bIssue(QList<double> issue)
	{
	}
#endif

QList<double> device::applyMouseSettings(QList<double> issue)
{
	issue[0]=issue[0]*(settings->value("mouse/reverseX").toBool()?-1:1)*(settings->value("mouse/speedX").toDouble()/200);
	issue[1]=issue[1]*(settings->value("mouse/reverseY").toBool()?-1:1)*(settings->value("mouse/speedY").toDouble()/200);

	return issue;
}

QList<double> device::applyKeyboardSettings(QList<double> issue)
{
	return issue;
}

QList<double> device::applySliderSettings(QList<double> issue)
{
	return issue;
}

QList<double> device::applyJoystickSettings(QList<double> issue)
{
	return issue;
}

void device::handleIssue(QList<double> issue)
{
	if(!issue.size())
		return;

	#ifdef __unix__
		if(deviceHandle<0) return;
	#endif

	switch(thisType)
	{
		case device::mouse:
			if(issue.size()!=6)
				return;
			#ifdef __unix__
				linuxHandleMouseIssue(applyMouseSettings(issue));
			#endif
			#ifdef __windows__
				windowsHandleMouseIssue(applyMouseSettings(issue));
			#endif
			break;

		case device::slider:
			#ifdef __unix__
				linuxHandleSliderIssue(applySliderSettings(issue));
			#endif
			#ifdef __windows__
				windowsHandleSliderIssue(applySliderSettings(issue));
			#endif
			break;

		case device::joystick1:
			#ifdef __unix__
				linuxHandleJoystick1Issue(applyJoystickSettings(issue));
			#endif
			#ifdef __windows__
				windowsHandleJoystick1Issue(applyJoystickSettings(issue));
			#endif
			break;

		case device::joystick2:
			#ifdef __unix__
				linuxHandleJoystick2Issue(applyJoystickSettings(issue));
			#endif
			#ifdef __windows__
				windowsHandleJoystick2Issue(applyJoystickSettings(issue));
			#endif
			break;

		case device::joystick2b:
			#ifdef __unix__
				linuxHandleJoystick2bIssue(applyJoystickSettings(issue));
			#endif
			#ifdef __windows__
				windowsHandleJoystick2bIssue(applyJoystickSettings(issue));
			#endif
			break;
	}
}

void device::handleControlSignal(QString value)
{
	QStringList list=value.split(QRegExp("<|>"), QString::SkipEmptyParts);
	emit report(list[0]+" set as "+list[1]);

	if(list[0]=="MODE")
	{
		if(list[1]=="mouse")
		{
			thisType=device::mouse;
			#ifdef __unix__
				linuxCreateMouseDevice();
			#endif
			return;
		}

		if(list[1]=="slider")
		{
			thisType=device::slider;
			#ifdef __unix__
				linuxCreateKeyboardDevice();
			#endif
			return;
		}

		if(list[1]=="1")
		{
			thisType=device::joystick1;
			#ifdef __unix__
				linuxCreateJoystickDevice();
			#endif		
			return;
		}

		if(list[1]=="2")
		{
			thisType=device::joystick2;
			#ifdef __unix__
				linuxCreateJoystickDevice();
			#endif		
			return;
		}

		if(list[1]=="2b")
		{
			thisType=device::joystick2b;
			#ifdef __unix__
				linuxCreateJoystickDevice();
			#endif		
			return;
		}
	}
}

void device::synchronize()
{
	settings->sync();
}
